package com.yinNuo.collectWool.core.config;

import com.yinNuo.collectWool.core.annotion.NoAuthAccess;
import com.yinNuo.collectWool.core.exception.BizExceptionEnum;
import com.yinNuo.collectWool.core.response.ErrorResponseData;
import com.yinNuo.collectWool.utils.JwtTokenUtil;
import com.yinNuo.collectWool.utils.RenderUtil;
import com.yinNuo.collectWool.utils.TokenUtil;
import io.jsonwebtoken.JwtException;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;

/**
 * @author: yinNuo
 * create at: 2021/3/29 15:03
 * @description: 自定义拦截器配置类 处理token
 */
@Component
public class Interceptor implements HandlerInterceptor {
    /**
     * 这个方法是在访问接口之前执行的，我们只需要在这里写验证登陆状态的业务逻辑，就可以在用户调用指定接口之前验证登陆状态了
     * 该方法在进入Handler方法执行之前执行此方法
     * 应用场景：如身份认证，身份授权。
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof org.springframework.web.servlet.resource.ResourceHttpRequestHandler) {
            //放行静态资源请求
            return true;
        }
        return check(request, response, handler);
    }
    /**
     * 验证token是否有效,包含了验证jwt是否正确
     *
     * @param request
     * @param response
     * @return
     */
    private boolean check(HttpServletRequest request, HttpServletResponse response, Object handler) {
        //如果不是映射到方法直接通过
        if (!(handler instanceof HandlerMethod)) {
            return true;
        }
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 判断接口是否需要登录认证
        NoAuthAccess methodAnnotation = method.getAnnotation(NoAuthAccess.class);
        // 有 @NoAuthAccess 注解，不需要认证
        if (methodAnnotation != null) {
            return true;
        }
        final String authToken = TokenUtil.getToken(request);
        if (authToken == null) {
            RenderUtil.renderJson(response, new ErrorResponseData(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return false;
        }
        //验证token是否过期,包含了验证jwt是否正确
        try {
            boolean flag = JwtTokenUtil.isTokenExpired(authToken);
            if (flag) {
                RenderUtil.renderJson(response, new ErrorResponseData(BizExceptionEnum.TOKEN_EXPIRED.getCode(), BizExceptionEnum.TOKEN_EXPIRED.getMessage()));
                return false;
            }
        } catch (JwtException e) {
            //有异常就是token解析失败
            RenderUtil.renderJson(response, new ErrorResponseData(BizExceptionEnum.TOKEN_ERROR.getCode(), BizExceptionEnum.TOKEN_ERROR.getMessage()));
            return false;
        }
        return true;
    }

    /**
     * 该方法在进入Handler方法之后，返回ModelAndView之前执行
     * 应用场景从modelAndView出发，将公用模型数据（如菜单导航）在这里传到视图，也可以在这里统一制定视图
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {

    }

    /**
     * 该方法在handler方法执行完之后执行
     * 应用场景：统一日志处理，统一异常处理
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {

    }
}
